/*
 * MPEGFile.cpp
 *      -- file holding a complete MPEG encoded data
 *
 * Author: Qiu Shuang
 * Date: Mar 2nd, 2011
 */
#include "MPEGFile.h"

#include "MPEGFrameHeader.h"
#include "MPEGFrameData.h"
#include "metadata.h"

#ifdef DEBUG
#include <stdio.h>

#include <fstream>

std::ofstream output("sideinfo_modified");
std::ofstream compress("compress_my");
std::ofstream huff_table("huff_table_my");
std::ofstream header("header_my");

std::ofstream huffman_decoded("huffman_decoded_my");
std::ofstream restored("restored_my");
std::ofstream jointstereo("jointstereo_my");

std::ofstream global_gain("global_gain");
std::ofstream scalescale("scalescale");

std::ofstream test("test");

FILE *scale_fact = fopen("scale_I_my_other", "w+");
#endif

/* external function declaration */
bool buildHuffmanTree();

/* constructor */
MPEGFile::MPEGFile(const char *file_name, bool parse):_contents(){
    if(file_name != NULL){              /* file name is not null */
        _mpeg = fopen(file_name, "rb"); /* open file in binary mode */

#ifdef DEBUG
    int count = 0;
#endif
        if(_mpeg == NULL){
            printf("Error occur when open file %s.\n", file_name);
        }else{                          /* file open successfully */
            _filename = file_name;      /* record filename */

            if(parse){                  /* parse the MPEG file instinctively */
                if(buildHuffmanTree()){
                    while(nextFrame() == OK){       /* decode all MPEG frame in a file */
                        _contents.push_back(new MPEGFrameData
                            (_mpeg, new MPEGFrameHeader(_header_buf)));
#ifdef DEBUG
    count++;
#endif
                    }
                }else{
                    printf("Unable to load huffman decoding tree.\n");

                    return;
                }
#ifdef DEBUG
    printf("total frame read %d\n", count);
#endif
                // we have reached EOF of the MPEG file
            }
        }
    }else{
        printf("Cannot open NULL file.\n");
    }
}

/* destructor */
MPEGFile::~MPEGFile(){
    if(_mpeg != NULL){      /* close open file */
        fclose(_mpeg);
    }

    std::list<MPEGFrameData *>::iterator itr_begin = _contents.begin();
    std::list<MPEGFrameData *>::iterator itr_end = _contents.end();
    while(itr_begin != itr_end){
        delete (*itr_begin);

        itr_begin++;
    }
}

/* skip current frame and move to next one */
int MPEGFile::nextFrame(){
    if(_mpeg == NULL){
        printf("No open MPEG file found.\n");

        return ERROR;
    }else{
        unsigned short buf = 0;     /* buffer for first synch_code */
        unsigned char one_byte = 0; /* buffer for one byte data */

#ifdef DEBUG
        unsigned int byte_read = 0; /* record #bytes read */
#endif

        /* find the first appearance of synch code */
        while(((buf & 0xfff0) != 0xfff0) && !feof(_mpeg)){
            if((buf & 0xff) == 0xff){
                fread((void *)&one_byte, 1, sizeof(unsigned char), _mpeg);
                buf = (buf << 8) | one_byte;
#ifdef DEBUG
                byte_read++;        /* one more byte read */
#endif
            }else{
                fread((void *)&buf, 1, sizeof(unsigned short), _mpeg);

                /* change byte order because of little-endian */
                buf = HIGH_TO_LOW(buf);
#ifdef DEBUG
                byte_read += sizeof(unsigned short);    /* 2 more bytes read */
#endif
            }
        }

        if(feof(_mpeg)){
            printf("EOF reached. Cannot extract necessary information.\n");

            return ERROR;
        }else{
            /* extract frame header into frame header buffer */
            /* treat frame header as raw byte data */
            *(unsigned short *)_header_buf = HIGH_TO_LOW(buf); 
            fread((void *)&buf, 1, sizeof(unsigned short), _mpeg);
            *(unsigned short *)(_header_buf + 2) = buf;

            _content_begin = ftell(_mpeg);
#ifdef DEBUG
            printf("number of bytes read %d\tcontent begin position %d\n", byte_read, _content_begin);
            printf("frame header %x%x%x%x\n", _header_buf[0], _header_buf[1], _header_buf[2], _header_buf[3]);
#endif
            return OK;
        }
    }
}